package gr.idocs.workshop;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Uses HashSets to merge files and default Collections method (single-threaded) for sorting
 * @author trifyllalex
 *
 */
public class MainWithHashSets {
	private static Logger logger = LoggerFactory.getLogger(MainWithHashSets.class);
	// private static String inFilePath1 = "inFile1.txt";
	// private static String inFilePath2 = "inFile2.txt";
	private static String inFilePath1 = "input1.txt";
	private static String inFilePath2 = "input2.txt";
	private static String outFilePath = "outFile.txt";
	private static Set<String> lines = new HashSet<String>();

	public static void main(String[] args) {
		
		long startTime = System.nanoTime();
		long totalStartTime = System.nanoTime();
		long endTime;
		long duration;
		if (validateArguments(args)) {
			inFilePath1 = args[0];
			inFilePath2 = args[1];
			outFilePath = args[2];
		}
		try {
			File input1 = new File(inFilePath1);
			File input2 = new File(inFilePath1);

			long sizeOfInput1 = input1.length();
			long sizeOfInput2 = input2.length();

			if (sizeOfInput1 <= sizeOfInput2) {
				createSetFromFile(inFilePath1);
				lines = mergeSetWithFile(inFilePath2);
			} else {
				createSetFromFile(inFilePath2);
				lines = mergeSetWithFile(inFilePath1);
			}
			List<String> list = new ArrayList<String>(lines);
			
			endTime = System.nanoTime();
			duration = endTime - startTime;
			//logger.info("merge Execution Time: " + duration / 1000000000F);
			startTime = System.nanoTime();

			Collections.sort(list);
			
			endTime = System.nanoTime();
			duration = endTime - startTime;
			logger.info("sort Execution Time: " + duration / 1000000000F);
			startTime = System.nanoTime();

			FileUtils.writeLines(new File(outFilePath), list);
			
			endTime = System.nanoTime();
			duration = endTime - totalStartTime;
			//logger.info("Approximate Method Execution Time: " + duration / 1000000000F);
			logger.info("-----------------------------------------------------");
			
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/*
	 * 
	 */
	private static void createSetFromFile(String inFilePath) throws IOException {

		InputStream fis = null;
		BufferedReader br = null;
		String line;
		try {
			fis = new FileInputStream(inFilePath);
			br = new BufferedReader(new InputStreamReader(fis));
			while ((line = br.readLine()) != null) {
				lines.add(line);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				br.close();
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}

	}

	private static Set<String> mergeSetWithFile(String inFilePath) {
		Set<String> retained = new HashSet<String>();
		InputStream fis = null;
		BufferedReader br = null;
		String line;
		try {
			fis = new FileInputStream(inFilePath);
			br = new BufferedReader(new InputStreamReader(fis));
			while ((line = br.readLine()) != null) {
				if (lines.contains(line))
					retained.add(line);
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				br.close();
				fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}

		return retained;
	}

	/*
	 * If an argument is found that is not a valid file path, or it is a path to a file that does not exist the method
	 * returns false.
	 */
	public static boolean validateArguments(String[] args) {
		boolean result = true;
		if (args.length != 3) {
			logger.info("Wrong number of arguments given. The default file paths will be used.");
			result = false;
		} else {
			for (int i = 0; i < args.length; i++) {

				String path = FilenameUtils.getPath(args[i]);
				if (path == null || path.equals("")) {
					//logger.info("Filepath '" + args[i] + "' does not exist or is invalid.");
					result = false;
				}
			}
		}
		return result;
	}

}
